﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using AppEnvironment.Lib.Interfaces;

namespace AppEnvironment.Lib
{
    public delegate void OnValidationErrorDelegate(IEnumerable<string> messages);

    public abstract class ValidationHandler : IValidationHandler
    {
        public event OnValidationErrorDelegate OnValidationError = delegate { };
        public event Action OnValidationClear = delegate { };

        internal List<string> List { get; set; }

        protected void AddError(string message)
        {
            List.Add(message);
        }

        protected void FireOnValidationError(IEnumerable<string> messages)
        {
            OnValidationError(messages);
        }

        protected void FireOnValidationClear()
        {
            OnValidationClear();
        }

        protected void EnforceMandatory(string value, string description)
        {
            EnforceMandatory(value, description, 0);
        }

        protected void EnforceMandatory(string value, string description, int maxLength)
        {
            if (string.IsNullOrEmpty(value))
            {
                AddError(description + " is required.");
            }
            else
            {
                if (maxLength > 0)
                {
                    if (value.Length > maxLength)
                    {
                        AddError(description + " is too long (max " + maxLength + " characters).");
                    }
                }
            }
        }

        private enum EmailFormatErrorEnum
        {
            Valid,
            Empty,
            TooLong,
            Invalid
        }

        private static EmailFormatErrorEnum IsValidEmail(string email, bool required, int maxLength)
        {
            if (required && string.IsNullOrEmpty(email))
            {
                return EmailFormatErrorEnum.Empty;
            }

            if (string.IsNullOrEmpty(email))
            {
                return EmailFormatErrorEnum.Valid;
            }

            if (maxLength > 0)
            {
                if (email.Length > maxLength)
                {
                    return EmailFormatErrorEnum.TooLong;
                }
            }

            string strRegex = @"[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)" +
                @"*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?";
            Regex re = new Regex(strRegex);
            if (re.IsMatch(email))
            {
                return EmailFormatErrorEnum.Valid;
            }
            else
            {
                return EmailFormatErrorEnum.Invalid;
            }
        }

        protected void ValidateEmail(string email, string fieldName, int maxLength, bool required)
        {
            ValidateEmail(email, fieldName, maxLength, required, false, null);
        }

        protected void ValidateEmail(string email, string fieldName, int maxLength, bool required, bool useConfirmation, string confirmationEmail)
        {
            EmailFormatErrorEnum result = IsValidEmail(email, required, maxLength);
            switch (result)
            {
                case EmailFormatErrorEnum.Empty:
                    AddError("Please provide " + fieldName);
                    break;
                case EmailFormatErrorEnum.TooLong:
                    AddError(fieldName + " is too long (max " + maxLength + " letters)");
                    break;
                case EmailFormatErrorEnum.Invalid:
                    AddError(fieldName + " is in an incorrect format");
                    break;
            }

            if (useConfirmation)
            {
                if ((string.IsNullOrEmpty(confirmationEmail)) ||
                    ((result == EmailFormatErrorEnum.Valid) && (email.ToLowerInvariant() != confirmationEmail.ToLowerInvariant())))
                {
                    AddError(
                        "Confirmation " + fieldName + " differs from the actual " + fieldName);
                }
            }
        }
    }

    public abstract class ValidationHandler<T> : ValidationHandler
    {
        public bool Validate(T dto)
        {
            return TestValidation(delegate
            {
                DoValidate(dto);
            });
        }

        protected bool TestValidation(Action action)
        {
            List = new List<string>();
            action();
            bool isValid = List.Count == 0;
            if (isValid)
            {
                FireOnValidationClear();
            }
            else
            {
                FireOnValidationError(List);
            }
            List = null;
            return isValid;
        }

        protected abstract void DoValidate(T dto);
    }
}
